/**
 *
 */
package cc.aileron.dao.utils;

import java.util.List;

import cc.aileron.commons.util.SkipList;
import cc.aileron.dao.G2DaoPager;
import cc.aileron.dao.G2DaoPagerLink;
import cc.aileron.dao.G2DaoPaging;

/**
 * ページャー生成ファクトリ
 * 
 * @author aileron
 */
public class G2DaoPagerFactory
{
    /**
     * @param pagingSize
     * @return {@link G2DaoPager}
     */
    public static G2DaoPager get(final int pagingSize)
    {
        return get(pagingSize, 10);
    }

    /**
     * @param pagingSize
     * @param pagingLinkSize
     * @return {@link G2DaoPager}
     */
    public static G2DaoPager get(final int pagingSize, final int pagingLinkSize)
    {
        final G2DaoPagingLocal paging = new G2DaoPagingLocal(pagingSize);
        return new G2DaoPager()
        {
            @Override
            public void baseUrl(final String baseUrl)
            {
                this.baseUrl = baseUrl;
            }

            @Override
            public G2DaoPaging context()
            {
                return paging;
            }

            @Override
            public int countAll()
            {
                return paging.count();
            }

            @Override
            public int countEnd()
            {
                final int countEnd = paging.offset() + paging.limit();
                return countEnd > countAll() ? countAll() : countEnd;
            }

            @Override
            public int countStart()
            {
                return paging.offset() + 1;
            }

            @Override
            public boolean isPageBack()
            {
                return paging.pageNumber() > 1;
            }

            @Override
            public boolean isPageNext()
            {
                return paging.pageNumber() < paging.pageMax();
            }

            @Override
            public String linkBack()
            {
                return baseUrl + (paging.pageNumber() - 1);
            }

            @Override
            public String linkNext()
            {
                return baseUrl + (paging.pageNumber() + 1);
            }

            @Override
            public List<G2DaoPagerLink> links()
            {
                final List<G2DaoPagerLink> links = new SkipList<G2DaoPagerLink>();
                final G2DaoPagerContext context = new G2DaoPagerContext(paging.pageNumber(),
                        paging.pageMax(),
                        pagingLinkSize);
                final int page = paging.pageNumber();
                final int start = context.start;
                final int end = context.end;
                for (int i = start; i <= end; i++)
                {
                    final String url = baseUrl + i;
                    final String label = String.valueOf(i);
                    final boolean isSelected = i == page;
                    links.add(new G2DaoPagerLink()
                    {
                        @Override
                        public String href()
                        {
                            return url;
                        }

                        @Override
                        public boolean isNotSelected()
                        {
                            return !isSelected;
                        }

                        @Override
                        public boolean isSelected()
                        {
                            return isSelected;
                        }

                        @Override
                        public String label()
                        {
                            return label;
                        }
                    });
                }
                return links;
            }

            @Override
            public int pageCount()
            {
                return paging.pageMax();
            }

            @Override
            public void pageNumber(final int pageNumber)
            {
                paging.pageNumber(pageNumber);
            }

            private String baseUrl;
        };
    }
}

class G2DaoPagerContext
{
    /**
     * @param page
     * @param maxPage
     * @param linkSize
     */
    public G2DaoPagerContext(final int page, final int maxPage,
            final int linkSize)
    {
        final int point = page;
        final int max = maxPage;
        final int allocateSize = linkSize / 2;

        final int forthAllocatePointPlan = point - allocateSize;
        final int backAllocatePointPlan = point + allocateSize - 1;

        final boolean isForthAllocate = forthAllocatePointPlan > 0;
        final boolean isBackAllocate = backAllocatePointPlan < max;

        final int forthAllocatePoint = isForthAllocate ? forthAllocatePointPlan
                : 1;
        final int backAllocatePoint = isBackAllocate ? backAllocatePointPlan
                : max;

        final int forthAllocateSize = point - forthAllocatePoint;
        final int backAllocateSize = backAllocatePoint - point;

        final int missForthAllocationSize = allocateSize - forthAllocateSize;
        final int missBackAllocationSize = allocateSize - backAllocateSize;

        if (isBackAllocate)
        {
            this.start = forthAllocatePoint;
        }
        else
        {
            final int tmp = forthAllocatePoint - missBackAllocationSize;
            this.start = tmp < 1 ? 1 : tmp;
        }

        if (isForthAllocate)
        {
            this.end = backAllocatePoint;
        }
        else
        {
            final int tmp = backAllocatePoint + missForthAllocationSize;
            this.end = tmp > max ? max : tmp;
        }
    }

    /**
     * end
     */
    public final int end;

    /**
     * start
     */
    public final int start;
}

class G2DaoPagingLocal implements G2DaoPaging
{
    public int count()
    {
        return count;
    }

    public void count(final int count)
    {
        this.count = count;
    }

    @Override
    public int limit()
    {
        return pageSize;
    }

    @Override
    public int offset()
    {
        return pageSize * (pageNumber() - 1);
    }

    public int pageMax()
    {
        final int pageCount = count / pageSize;
        return count % pageSize == 0 ? pageCount : pageCount + 1;
    }

    public int pageNumber()
    {
        final int pageMax = pageMax();
        if (pageNumber <= pageMax && 0 < pageNumber)
        {
            return pageNumber;
        }
        else
        {
            return 1;
        }
    }

    /**
     * @param pageNumber
     */
    public void pageNumber(final int pageNumber)
    {
        this.pageNumber = pageNumber;
    }

    public G2DaoPagingLocal(final int pageSize)
    {
        this.pageSize = pageSize;
    }

    private int count;

    private int pageNumber = 1;
    private final int pageSize;
}